#include <bits/stdc++.h>
using namespace std;

// 定义模数值
const int MOD = 998244353;
using LL = long long;

// 取模运算，确保结果在[0, MOD)范围内
int mod(int a) { return a >= MOD ? a - MOD : a; }

// 快速幂运算：计算(a^k) % MOD
int power(int a, int k) {
  int result = 1;
  while (k > 0) {
    if (k & 1) {
      result = 1LL * result * a % MOD;
    }
    a = 1LL * a * a % MOD;
    k >>= 1;
  }
  return result;
}

// 线段树结构，支持区间更新和查询
struct SegmentTree {
  // 线段树节点信息
  vector<int> left, right, mid, lazy, tree;

  // 构造函数，初始化线段树大小
  SegmentTree(int n)
      : left(n << 3), right(n << 3), mid(n << 3), lazy(n << 3, -1),
        tree(n << 3) {}

  // 更新当前节点值为左右子节点之和
  void updateCurrentNode(int node) {
    tree[node] = mod(tree[node << 1] + tree[node << 1 | 1]);
  }

  // 构建线段树
  void build(int node, int l, int r, const vector<int> &values) {
    left[node] = l;
    right[node] = r;
    mid[node] = (l + r) >> 1;

    if (l == r) {
      tree[node] = values[l];
      return;
    }

    build(node << 1, l, mid[node], values);
    build(node << 1 | 1, mid[node] + 1, r, values);
    updateCurrentNode(node);
  }

  // 推送懒标记到子节点
  void pushDown(int node, int value) {
    int rangeLength = right[node] - left[node] + 1;
    tree[node] = 1LL * value * rangeLength % MOD;
    lazy[node] = value;
  }

  // 检查并推送懒标记
  void checkAndPushDown(int node) {
    if (lazy[node] != -1) {
      pushDown(node << 1, lazy[node]);
      pushDown(node << 1 | 1, lazy[node]);
      lazy[node] = -1;
    }
  }

  // 区间更新：将[l, r]范围的值设为d
  void rangeUpdate(int node, int l, int r, int d) {
    if (left[node] >= l && right[node] <= r) {
      return pushDown(node, d);
    }

    checkAndPushDown(node);
    if (mid[node] >= l) {
      rangeUpdate(node << 1, l, r, d);
    }
    if (mid[node] < r) {
      rangeUpdate(node << 1 | 1, l, r, d);
    }
    updateCurrentNode(node);
  }

  // 区间查询：获取[l, r]范围的和
  LL rangeQuery(int node, int l, int r) {
    if (left[node] >= l && right[node] <= r) {
      return tree[node];
    }

    checkAndPushDown(node);
    if (mid[node] >= r) {
      return rangeQuery(node << 1, l, r);
    }
    if (mid[node] < l) {
      return rangeQuery(node << 1 | 1, l, r);
    }
    return mod(rangeQuery(node << 1, l, r) + rangeQuery(node << 1 | 1, l, r));
  }

  // 查找区间内满足条件的值（当前实现可能需要进一步验证）
  int findInRange(int node, int l, int r, int d) {
    if (tree[node] <= d) {
      return tree[node];
    }
    if (tree[node << 1] >= d) {
      return findInRange(node << 1, l, r, d);
    }
    return tree[node << 1] +
           findInRange(node << 1 | 1, l, r, d - tree[node << 1]);
  }
};

// 解决问题的主函数
void solve() {
  int n, m;
  cin >> n >> m;

  vector<int> values(n + 1);
  for (int i = 1; i <= n; i++) {
    cin >> values[i];
  }

  SegmentTree segmentTree(n);
  segmentTree.build(1, 1, n, values);

  for (int i = 1; i <= m; i++) {
    int a, b;
    cin >> a >> b;

    // 计算区间[a, b]的平均值
    LL sum = segmentTree.rangeQuery(1, a, b) % MOD;
    int length = b - a + 1;
    int average = sum * power(length, MOD - 2) % MOD;

    // 将区间[a, b]的值更新为平均值
    segmentTree.rangeUpdate(1, a, b, average);
  }

  // 输出最终结果
  for (int i = 1; i <= n; i++) {
    cout << segmentTree.rangeQuery(1, i, i) << " ";
  }
  cout << endl; // 这里其实可以在线段树内操作优化到O(n)
}

int main() {
  solve();
  return 0;
}